import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common';
import { createClient, RedisClientType } from 'redis';
import type { CacheInterface, CacheConfig } from '../../cache/cache.interface';
import { CacheStats } from '../../cache/cache.interface';

/**
 * Redis 服务
 * 基于 NestJS 官方示例实现
 * 参考: https://docs.nestjs.cn/fundamentals/dependency-injection
 * 对应 Java: RedisUtils
 */
@Injectable()
export class RedisService implements CacheInterface, OnModuleDestroy {
  private readonly logger = new Logger(RedisService.name);
  private client: RedisClientType;
  private cacheStats: CacheStats = {
    hits: 0,
    misses: 0,
    keys: 0,
    memory: 0,
    uptime: 0,
  };
  private startTime: number;

  constructor(private readonly config: CacheConfig) {
    this.startTime = Date.now();
    this.initializeClient();
  }

  private async initializeClient() {
    try {
      this.client = createClient({
        socket: {
          host: this.config.host,
          port: this.config.port,
        },
        password: this.config.password,
        database: this.config.db || 0,
      });

      this.client.on('error', (err) => {
        this.logger.error('Redis Client Error', err);
      });

      this.client.on('connect', () => {
        this.logger.log('Redis Client Connected');
      });

      this.client.on('ready', () => {
        this.logger.log('Redis Client Ready');
      });

      this.client.on('end', () => {
        this.logger.log('Redis Client Disconnected');
      });

      await this.client.connect();
    } catch (error) {
      this.logger.error('Failed to initialize Redis client', error);
      throw error;
    }
  }

  async onModuleDestroy() {
    if (this.client) {
      await this.client.quit();
    }
  }

  /**
   * 获取缓存
   */
  async get<T = any>(key: string): Promise<T | null> {
    try {
      const prefixedKey = this.getPrefixedKey(key);
      const value = await this.client.get(prefixedKey);

      if (value === null) {
        this.cacheStats.misses++;
        return null;
      }

      this.cacheStats.hits++;
      return JSON.parse(value);
    } catch (error) {
      this.logger.error(`Failed to get cache key: ${key}`, error);
      this.cacheStats.misses++;
      return null;
    }
  }

  /**
   * 设置缓存
   */
  async set(key: string, value: any, ttl?: number): Promise<void> {
    try {
      const prefixedKey = this.getPrefixedKey(key);
      const serializedValue = JSON.stringify(value);

      if (ttl) {
        await this.client.setEx(prefixedKey, ttl, serializedValue);
      } else {
        await this.client.set(prefixedKey, serializedValue);
      }
    } catch (error) {
      this.logger.error(`Failed to set cache key: ${key}`, error);
      throw error;
    }
  }

  /**
   * 删除缓存
   */
  async del(key: string): Promise<void> {
    try {
      const prefixedKey = this.getPrefixedKey(key);
      await this.client.del(prefixedKey);
    } catch (error) {
      this.logger.error(`Failed to delete cache key: ${key}`, error);
      throw error;
    }
  }

  /**
   * 批量删除缓存
   */
  async delMany(keys: string[]): Promise<void> {
    try {
      const prefixedKeys = keys.map((key) => this.getPrefixedKey(key));
      await this.client.del(prefixedKeys);
    } catch (error) {
      this.logger.error(
        `Failed to delete cache keys: ${keys.join(', ')}`,
        error,
      );
      throw error;
    }
  }

  /**
   * 检查缓存是否存在
   */
  async exists(key: string): Promise<boolean> {
    try {
      const prefixedKey = this.getPrefixedKey(key);
      const result = await this.client.exists(prefixedKey);
      return result === 1;
    } catch (error) {
      this.logger.error(`Failed to check cache key: ${key}`, error);
      return false;
    }
  }

  /**
   * 设置过期时间
   */
  async expire(key: string, ttl: number): Promise<void> {
    try {
      const prefixedKey = this.getPrefixedKey(key);
      await this.client.expire(prefixedKey, ttl);
    } catch (error) {
      this.logger.error(`Failed to set expire for cache key: ${key}`, error);
      throw error;
    }
  }

  /**
   * 获取过期时间
   */
  async ttl(key: string): Promise<number> {
    try {
      const prefixedKey = this.getPrefixedKey(key);
      return await this.client.ttl(prefixedKey);
    } catch (error) {
      this.logger.error(`Failed to get ttl for cache key: ${key}`, error);
      return -1;
    }
  }

  /**
   * 获取所有键
   */
  async keys(pattern?: string): Promise<string[]> {
    try {
      const searchPattern = pattern
        ? this.getPrefixedKey(pattern)
        : `${this.config.keyPrefix || 'cache'}:*`;
      const keys = await this.client.keys(searchPattern);
      return keys.map((key) => this.removePrefix(key));
    } catch (error) {
      this.logger.error(
        `Failed to get cache keys with pattern: ${pattern}`,
        error,
      );
      return [];
    }
  }

  /**
   * 清空所有缓存
   */
  async flush(): Promise<void> {
    try {
      await this.client.flushDb();
    } catch (error) {
      this.logger.error('Failed to flush cache', error);
      throw error;
    }
  }

  /**
   * 获取缓存统计信息
   */
  async stats(): Promise<CacheStats> {
    try {
      const info = await this.client.info('memory');
      const memoryMatch = info.match(/used_memory:(\d+)/);
      const memory = memoryMatch ? parseInt(memoryMatch[1]) : 0;

      const keyCount = await this.client.dbSize();

      return {
        ...this.cacheStats,
        keys: keyCount,
        memory,
        uptime: Date.now() - this.startTime,
      };
    } catch (error) {
      this.logger.error('Failed to get cache stats', error);
      return this.cacheStats;
    }
  }

  /**
   * 批量获取
   */
  async mget<T = any>(keys: string[]): Promise<(T | null)[]> {
    try {
      const prefixedKeys = keys.map((key) => this.getPrefixedKey(key));
      const values = await this.client.mGet(prefixedKeys);

      return values.map((value) => {
        if (value === null) {
          this.cacheStats.misses++;
          return null;
        }
        this.cacheStats.hits++;
        return JSON.parse(value);
      });
    } catch (error) {
      this.logger.error(`Failed to mget cache keys: ${keys.join(', ')}`, error);
      return keys.map(() => null);
    }
  }

  /**
   * 批量设置
   */
  async mset(
    keyValuePairs: Array<{ key: string; value: any; ttl?: number }>,
  ): Promise<void> {
    try {
      const pipeline = this.client.multi();

      for (const { key, value, ttl } of keyValuePairs) {
        const prefixedKey = this.getPrefixedKey(key);
        const serializedValue = JSON.stringify(value);

        if (ttl) {
          pipeline.setEx(prefixedKey, ttl, serializedValue);
        } else {
          pipeline.set(prefixedKey, serializedValue);
        }
      }

      await pipeline.exec();
    } catch (error) {
      this.logger.error('Failed to mset cache', error);
      throw error;
    }
  }

  /**
   * 原子递增
   */
  async incr(key: string, increment: number = 1): Promise<number> {
    try {
      const prefixedKey = this.getPrefixedKey(key);
      return await this.client.incrBy(prefixedKey, increment);
    } catch (error) {
      this.logger.error(`Failed to incr cache key: ${key}`, error);
      throw error;
    }
  }

  /**
   * 原子递减
   */
  async decr(key: string, decrement: number = 1): Promise<number> {
    return this.incr(key, -decrement);
  }

  /**
   * 设置哈希字段
   */
  async hset(key: string, field: string, value: any): Promise<void> {
    try {
      const prefixedKey = this.getPrefixedKey(key);
      const serializedValue = JSON.stringify(value);
      await this.client.hSet(prefixedKey, field, serializedValue);
    } catch (error) {
      this.logger.error(
        `Failed to hset cache key: ${key}, field: ${field}`,
        error,
      );
      throw error;
    }
  }

  /**
   * 获取哈希字段
   */
  async hget<T = any>(key: string, field: string): Promise<T | null> {
    try {
      const prefixedKey = this.getPrefixedKey(key);
      const value = await this.client.hGet(prefixedKey, field);

      if (value === null) {
        this.cacheStats.misses++;
        return null;
      }

      this.cacheStats.hits++;
      return JSON.parse(value);
    } catch (error) {
      this.logger.error(
        `Failed to hget cache key: ${key}, field: ${field}`,
        error,
      );
      this.cacheStats.misses++;
      return null;
    }
  }

  /**
   * 删除哈希字段
   */
  async hdel(key: string, field: string): Promise<void> {
    try {
      const prefixedKey = this.getPrefixedKey(key);
      await this.client.hDel(prefixedKey, field);
    } catch (error) {
      this.logger.error(
        `Failed to hdel cache key: ${key}, field: ${field}`,
        error,
      );
      throw error;
    }
  }

  /**
   * 获取哈希所有字段
   */
  async hgetall(key: string): Promise<Record<string, any>> {
    try {
      const prefixedKey = this.getPrefixedKey(key);
      const hash = await this.client.hGetAll(prefixedKey);

      const result: Record<string, any> = {};
      for (const [field, value] of Object.entries(hash)) {
        result[field] = JSON.parse(value);
      }

      return result;
    } catch (error) {
      this.logger.error(`Failed to hgetall cache key: ${key}`, error);
      return {};
    }
  }

  /**
   * 发布消息
   */
  async publish(channel: string, message: any): Promise<number> {
    try {
      const serializedMessage = JSON.stringify(message);
      return await this.client.publish(channel, serializedMessage);
    } catch (error) {
      this.logger.error(
        `Failed to publish message to channel: ${channel}`,
        error,
      );
      throw error;
    }
  }

  /**
   * 订阅频道
   */
  async subscribe(
    channel: string,
    callback: (message: any) => void,
  ): Promise<void> {
    try {
      await this.client.subscribe(channel, (message) => {
        try {
          const parsedMessage = JSON.parse(message);
          callback(parsedMessage);
        } catch (error) {
          this.logger.error(
            `Failed to parse message from channel: ${channel}`,
            error,
          );
        }
      });
    } catch (error) {
      this.logger.error(`Failed to subscribe to channel: ${channel}`, error);
      throw error;
    }
  }

  /**
   * 取消订阅
   */
  async unsubscribe(channel: string): Promise<void> {
    try {
      await this.client.unsubscribe(channel);
    } catch (error) {
      this.logger.error(
        `Failed to unsubscribe from channel: ${channel}`,
        error,
      );
      throw error;
    }
  }

  /**
   * 获取带前缀的键
   */
  private getPrefixedKey(key: string): string {
    const prefix = this.config.keyPrefix || 'cache';
    return `${prefix}:${key}`;
  }

  /**
   * 移除键前缀
   */
  private removePrefix(key: string): string {
    const prefix = this.config.keyPrefix || 'cache';
    return key.startsWith(`${prefix}:`)
      ? key.substring(prefix.length + 1)
      : key;
  }

  /**
   * 获取原始 Redis 客户端
   */
  getClient(): RedisClientType {
    return this.client;
  }

  /**
   * 检查连接状态
   */
  async isConnected(): Promise<boolean> {
    try {
      await this.client.ping();
      return true;
    } catch (error) {
      return false;
    }
  }

  /**
   * 重连
   */
  async reconnect(): Promise<void> {
    try {
      if (this.client) {
        await this.client.quit();
      }
      await this.initializeClient();
    } catch (error) {
      this.logger.error('Failed to reconnect Redis client', error);
      throw error;
    }
  }
}
